﻿using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UPMS.Core.Domain;

namespace UPMS.Core.Dao
{
    public abstract class BaseDao<TDomain, TId> where TDomain : DomainBase
    {
        public virtual void Insert(TDomain domain)
        {
            Execute(ctx =>
            {
                try
                {
                    ctx.Set<TDomain>().Add(domain);
                    ctx.SaveChanges();
                }
                catch (Exception ex)
                {
                    throw;
                }
            });
        }

        public virtual IList<TDomain> Select()
        {
            return Execute(ctx =>
            {
                return ctx.Set<TDomain>().ToArray();
            });
        }

        public virtual TDomain Select(TId id)
        {
            return Execute(ctx =>
            {
                return ctx.Set<TDomain>().Find(id);
            });
        } 

        public virtual void Update(TDomain domain)
        {
            Execute(ctx =>
            {
                ctx.Set<TDomain>().Attach(domain);
                ctx.Entry<TDomain>(domain).State = EntityState.Modified;
                ctx.SaveChanges();
            });
        }

        public virtual void Delete(TDomain domain)
        {
            Execute(ctx =>
            {
                ctx.Set<TDomain>().Attach(domain);
                ctx.Entry<TDomain>(domain).State = EntityState.Deleted;
                ctx.SaveChanges();
            });
        }
        public virtual void Delete(IList<TDomain> domains)
        {
            Execute(ctx =>
            {
                foreach (var d in domains)
                {
                    ctx.Set<TDomain>().Attach(d);
                    ctx.Entry<TDomain>(d).State = EntityState.Deleted;
                }

                ctx.SaveChanges();
            });
        }

        public virtual void Delete(TId id)
        {
            Execute(ctx =>
            {
                var domains = ctx.Set<TDomain>();
                var domain = domains.Find(id);
                domains.Remove(domain);
                ctx.SaveChanges();
            });
        }

        public virtual void Delete(IList<TId> ids)
        {

            Execute(ctx =>
            {
                var domains = ctx.Set<TDomain>();

                foreach (var id in ids)
                {
                    domains.Remove(domains.Find(id));
                }

                ctx.SaveChanges();
            });
        }

        internal static void Execute(Action<UpmsContext> action)
        {
            Execute(ctx =>
            {
                action(ctx);
                return (object)null;
            });
        }

        internal static T Execute<T>(Func<UpmsContext, T> func)
        {
            var database = default(UpmsContext);

            try
            {
                database = new UpmsContext();
                return func(database);
            }
            catch (Exception ex)
            {
                if (ex is DaoException)
                {
                    throw;
                }

                throw new DaoException(ex.Message, ex);
            }
            finally
            {
                if (database != null)
                {
                    database.Dispose();
                }
            }
        }
    }
}
